package controllers

import (
    "gouniapp/configs"
    "archive/zip"
    "crypto/rand"
    "errors"
    "fmt"
    "io"
    "mime"
    "net/http"
    "os"
    "path/filepath"
    "sync"
    "strings"
    "log"
    "time"
    "regexp"
    "github.com/h2non/filetype"
    "github.com/google/uuid"
    "github.com/gin-gonic/gin"
)

const (
    MaxPreviewSize = 2 * 1024 * 1024 // 2MB
    TempPath       = "./temp"
)

var (
    AllowedExts = map[string]bool{
        ".txt": true, ".php": true, ".js": true, ".html": true, 
        ".css": true, ".py": true, ".java": true, ".xml": true, ".json": true,
    }
    UploadPath string
)

func init() {
    uploadPath := filepath.Join(".", "uploads")
    absPath, err := filepath.Abs(uploadPath)
    if err != nil {
        panic(fmt.Sprintf("无法获取上传目录绝对路径: %v", err))
    }
    UploadPath = absPath
}

type FileRequest struct {
	Path     string `form:"path"`
	Keyword  string `form:"keyword"`
	Page     int    `form:"page"`
	PageSize int    `form:"pageSize"`
}

type FileInfo struct {
	Name     string    `json:"name"`
	Type     string    `json:"type"`
	Size     int64     `json:"size"`
	ModTime  time.Time `json:"modTime"`
	Ext      string    `json:"ext"`
	FullPath string    `json:"fullPath"`
	IsDir    bool      `json:"isDir"`
}

func isValidPath(path string) bool {
    // 获取绝对路径并清理
    absPath, err := filepath.Abs(path)
    if err != nil {
        return false
    }
    cleanAbsPath := filepath.Clean(absPath)
    // 检查是否位于上传目录下
    if !strings.HasPrefix(cleanAbsPath, UploadPath) {
        return false
    }
    // 保留字符校验（检查原始路径参数）
    forbiddenChars := []string{"..", ":", "*", "?", "\"", "<", ">", "|"}
    for _, char := range forbiddenChars {
        if strings.Contains(path, char) {
            return false
        }
    }
    // 特殊目录校验（如隐藏目录）
    parts := strings.Split(cleanAbsPath, string(filepath.Separator))
    for _, part := range parts {
        if strings.HasPrefix(part, ".") && part != "." && part != ".." {
            return false
        }
    }
    return true
}

func fileExists(path string) bool {
    _, err := os.Stat(path)
    return !os.IsNotExist(err)
}

func generateUUID() string {
    return uuid.New().String()
}

func handleFileError(c *gin.Context, err error, operation string) {
    var msg string
    switch {
    case os.IsNotExist(err):
        msg = "文件不存在"
    case os.IsPermission(err):
        msg = "权限不足"
    case errors.Is(err, os.ErrExist):
        msg = "文件已存在"
    default:
        msg = "操作失败"
    }
    JsonResponse(c, http.StatusInternalServerError, fmt.Sprintf("%s失败: %s", operation, msg), nil)
}

// 辅助函数：判断是否目录
func isDirectory(path string) bool {
    fi, err := os.Stat(path)
    return err == nil && fi.IsDir()
}

// 辅助函数：判断目录是否包含子目录
func hasSubdirectories(path string) bool {
    entries, err := os.ReadDir(path)
    if err != nil {
        return false
    }
    for _, entry := range entries {
        // 过滤特殊目录和隐藏文件
        if entry.IsDir() && entry.Name() != "." && entry.Name() != ".." && !strings.HasPrefix(entry.Name(), ".") {
            return true
        }
    }
    return false
}

// 目录信息结构体
type DirectoryInfo struct {
    Name     string `json:"name"`
    Path     string `json:"path"`      // 相对路径（相对于UploadPath）
    HasChild bool   `json:"hasChild"`  // 是否有子目录
}

func handleDirError(c *gin.Context, err error) {
    var msg string
    switch {
    case os.IsPermission(err):
        msg = "目录访问权限不足"
    case os.IsNotExist(err):
        msg = "目录不存在"
    case errors.Is(err, os.ErrInvalid):
        msg = "无效目录路径"
    default:
        msg = "目录读取失败"
    }
    JsonResponse(c, http.StatusInternalServerError, msg, nil)
}

func isValidDirectoryName(name string) bool {
    if name == "" || len(name) > 255 {
        return false
    }
    // 排除特殊字符 \ / : * ? " < > | 
    forbiddenChars := regexp.MustCompile(`[\\/:*?"<>|]`)
    return !forbiddenChars.MatchString(name)
}
type CreateDirectoryRequest struct {
    Path string `json:"path" binding:"required"`  // 当前所在路径
    Name string `json:"name" binding:"required"`  // 新目录或新文件名称
}

//创建目录
func CreateDirectory(c *gin.Context) {
    var req CreateDirectoryRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        JsonResponse(c, http.StatusBadRequest, "请求参数错误", nil)
        return
    }
    // 校验目录名称
    if !isValidDirectoryName(req.Name) {
        JsonResponse(c, http.StatusBadRequest, "目录名称包含非法字符", nil)
        return
    }
    // 构造完整路径
    fullPath := filepath.Join(UploadPath, filepath.Clean(req.Path), filepath.Clean(req.Name),)
    // 安全检查：防止路径遍历
    if !strings.HasPrefix(fullPath, UploadPath) {
        JsonResponse(c, http.StatusBadRequest, "非法路径", nil)
        return
    }
    // 检查目录是否存在
    if _, err := os.Stat(fullPath); !os.IsNotExist(err) {
        JsonResponse(c, http.StatusConflict, "目录已存在", nil)
        return
    }
    // 创建目录（权限0755）
    if err := os.MkdirAll(fullPath, 0755); err != nil {
        log.Printf("目录创建失败: %v 路径: %s", err, fullPath)
        JsonResponse(c, http.StatusInternalServerError, "目录创建失败", nil)
        return
    }
    JsonResponse(c, http.StatusOK, "目录创建成功", gin.H{"path": strings.TrimPrefix(fullPath, UploadPath),})
}

//创建文件
func CreateFile(c *gin.Context) {
    type CreateDirectoryRequest struct {
        Path string `json:"path" binding:"required"`  // 当前所在路径
        Name string `json:"name" binding:"required"`  // 新目录名称
    }
    var req CreateDirectoryRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        JsonResponse(c, http.StatusBadRequest, "请求参数错误", nil)
        return
    }
    // 校验文件名（复用校验函数，按需调整）
    if !isValidDirectoryName(req.Name) { // 假设校验规则相同
        JsonResponse(c, http.StatusBadRequest, "文件名包含非法字符", nil)
        return
    }
    // 构造完整文件路径
    fullPath := filepath.Join(UploadPath, filepath.Clean(req.Path), filepath.Clean(req.Name))
    // 防止路径遍历
    if !strings.HasPrefix(fullPath, UploadPath) {
        JsonResponse(c, http.StatusBadRequest, "非法路径", nil)
        return
    }
    // 检查文件是否已存在
    if _, err := os.Stat(fullPath); !os.IsNotExist(err) {
        JsonResponse(c, http.StatusConflict, "文件已存在", nil) // 修改提示
        return
    }
    // 确保父目录存在
    parentDir := filepath.Dir(fullPath)
    if err := os.MkdirAll(parentDir, 0755); err != nil {
        log.Printf("创建父目录失败: %v 路径: %s", err, parentDir)
        JsonResponse(c, http.StatusInternalServerError, "文件创建失败", nil)
        return
    }
    // 创建空文件
    file, err := os.Create(fullPath)
    if err != nil {
        log.Printf("文件创建失败: %v 路径: %s", err, fullPath)
        JsonResponse(c, http.StatusInternalServerError, "文件创建失败", nil)
        return
    }
    defer file.Close()
    JsonResponse(c, http.StatusOK, "文件创建成功", gin.H{"path": strings.TrimPrefix(fullPath, UploadPath)})
}

// 保存文本文件
func SaveTxt(c *gin.Context) {
    type TxtRequest struct {
        Path    string `json:"path" binding:"required"`
        Content string `json:"content" binding:"required"`
    }
    var req TxtRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        JsonResponse(c, http.StatusBadRequest, "请求参数错误", nil)
        return
    }
    // 安全校验路径
    cleanPath := filepath.Clean(req.Path)
    fullPath := filepath.Join(UploadPath, cleanPath)
    // 防止路径遍历攻击
    if !strings.HasPrefix(fullPath, UploadPath) {
        JsonResponse(c, http.StatusBadRequest, "非法路径", nil)
        return
    }
    // 校验文件类型
    if !AllowedExts[strings.ToLower(filepath.Ext(fullPath))] {
        JsonResponse(c, http.StatusBadRequest, "不支持该类型文件编辑", nil)
        return
    }
    // 检查文件是否存在
    if _, err := os.Stat(fullPath); os.IsNotExist(err) {
        JsonResponse(c, http.StatusNotFound, "文件不存在", nil)
        return
    }
    // 获取文件信息校验权限
    fileInfo, err := os.Stat(fullPath)
    if err != nil {
        log.Printf("获取文件信息失败: %v 路径: %s", err, fullPath)
        JsonResponse(c, http.StatusInternalServerError, "文件访问失败", nil)
        return
    }
    // 检查是否可写
    if fileInfo.Mode().Perm()&0200 == 0 {
        JsonResponse(c, http.StatusForbidden, "文件不可写", nil)
        return
    }
    
    // 直接打开文件进行写入
    file, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_TRUNC, 0644)
    if err != nil {
        log.Printf("打开文件失败: %v 路径: %s", err, fullPath)
        JsonResponse(c, http.StatusInternalServerError, "文件打开失败", nil)
        return
    }
    defer file.Close()

    // 判断是否是 .txt 文件，如果是，则统一换行符为LF
    normalizedContent := req.Content
    if strings.ToLower(filepath.Ext(fullPath)) == ".txt" {
        normalizedContent = strings.ReplaceAll(req.Content, "\r\n", "\n")
    }
    if _, err := file.WriteString(normalizedContent); err != nil {
        log.Printf("写入文件失败: %v 路径: %s", err, fullPath)
        JsonResponse(c, http.StatusInternalServerError, "文件保存失败", nil)
        return
    }
    // 更新修改时间
    now := time.Now()
    if err := os.Chtimes(fullPath, now, now); err != nil {
        log.Printf("更新文件时间失败: %v 路径: %s", err, fullPath)
    }
    // 去除执行权限
    if err := os.Chmod(fullPath, 0644); err != nil {
        log.Printf("设置文件权限失败: %v", err)
    }
    JsonResponse(c, http.StatusOK, "文件保存成功", gin.H{
        "path":    cleanPath,
        "size":    len(normalizedContent),
        "modTime": now.Format(time.RFC3339),
    })
}

// 获取目录列表（专为树形控件优化）
func GetDirectoryList(c *gin.Context) {
    // 目录列表请求
    type DirectoryListRequest struct {
        ParentPath string `json:"path"` // 当前父路径（空表示根目录）
    }
    var req DirectoryListRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        JsonResponse(c, http.StatusBadRequest, "参数错误", nil)
        return
    }
    // 构建安全路径
    basePath := filepath.Join(UploadPath, req.ParentPath)
    basePath = filepath.Clean(basePath)
    if !isValidPath(basePath) {
        JsonResponse(c, http.StatusForbidden, "非法路径访问", nil)
        return
    }
    if !isDirectory(basePath) {
        JsonResponse(c, http.StatusBadRequest, "路径不是目录", nil)
        return
    }
    entries, err := os.ReadDir(basePath)
    if err != nil {
        handleDirError(c, err)
        return
    }
    dirs := make([]DirectoryInfo, 0)
    for _, entry := range entries {
        if entry.IsDir() {
            entryName := entry.Name()
            // 过滤系统保留目录和隐藏目录
            if entryName == "." || entryName == ".." || strings.HasPrefix(entryName, ".") {
                continue
            }
            absEntryPath := filepath.Join(basePath, entryName)
            // 生成相对于UploadPath的相对路径
            relPath, err := filepath.Rel(UploadPath, absEntryPath)
            if err != nil || !isValidPath(absEntryPath) {
                continue
            }
            dirs = append(dirs, DirectoryInfo{
                Name:     entryName,
                Path:     filepath.ToSlash(relPath), // 统一使用斜杠
                HasChild: hasSubdirectories(absEntryPath),
            })
        }
    }
    JsonResponse(c, http.StatusOK, "获取成功", gin.H{"items": dirs})
}

// 文件列表
func GetFileList(c *gin.Context) {
	var req FileRequest
	if err := c.ShouldBindJSON(&req); err != nil {
	    JsonResponse(c, http.StatusBadRequest, "参数错误", nil)
		return
	}
	// 构建安全路径
	basePath := filepath.Clean(filepath.Join(UploadPath, req.Path))
	if !strings.HasPrefix(basePath, UploadPath) {
	    JsonResponse(c, http.StatusInternalServerError, "非法路径访问", nil)
		return
	}
	// 读取目录
	entries, err := os.ReadDir(basePath)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
		    JsonResponse(c, http.StatusInternalServerError, "目录不存在", nil)
			return
		}
		JsonResponse(c, http.StatusInternalServerError, "目录读取失败", nil)
		return
	}
	// 处理文件和分页
	var files []FileInfo
	for _, entry := range entries {
		info, err := entry.Info()
		if err != nil {
			continue
		}
		// 过滤搜索关键词
		if req.Keyword != "" && !strings.Contains(info.Name(), req.Keyword) {
			continue
		}
		fileType := "file"
		ext := ""
		if entry.IsDir() {
			fileType = "directory"
		} else {
			ext = strings.ToLower(strings.TrimPrefix(filepath.Ext(info.Name()), "."))
		}
		files = append(files, FileInfo{
			Name:     info.Name(),
			Type:     fileType,
			Size:     info.Size(),
			ModTime:  info.ModTime(),
			Ext:      ext,
			FullPath: filepath.Join(req.Path, info.Name()),
			IsDir:    entry.IsDir(),
		})
	}
	// 分页处理
	start := (req.Page - 1) * req.PageSize
	if start < 0 || start >= len(files) {
		start = 0
	}
	end := start + req.PageSize
	if end > len(files) {
		end = len(files)
	}
    JsonResponse(c, http.StatusOK, "获取成功", gin.H{
		"items": files[start:end],
		"total": len(files),
	})
}

// 文件删除
func DeleteFile(c *gin.Context) {
    type Request struct {
        Path string `json:"path" binding:"required"`
    }
    var req Request
    if err := c.ShouldBindJSON(&req); err != nil {
        JsonResponse(c, http.StatusBadRequest, "无效的请求参数", nil)
        return
    }
    // 路径验证
    targetPath := filepath.Join(UploadPath, req.Path)
    if !strings.HasPrefix(filepath.Clean(targetPath)+string(os.PathSeparator), 
       filepath.Clean(UploadPath)+string(os.PathSeparator)) {
        JsonResponse(c, http.StatusForbidden, "非法路径访问", nil)
        return
    }
    // 删除操作
    var err error
    if err = os.RemoveAll(targetPath); err != nil {
        var pathErr *os.PathError
        if errors.As(err, &pathErr) {
            switch {
            case os.IsPermission(err):
                JsonResponse(c, http.StatusForbidden, "权限不足", nil)
                return
            case os.IsNotExist(err):
                JsonResponse(c, http.StatusNotFound, "文件不存在", nil)
                return
            }
        }
        JsonResponse(c, http.StatusInternalServerError, fmt.Sprintf("删除失败: %v", err), nil)
        return
    }
    JsonResponse(c, http.StatusOK, "删除成功", nil)
}

// 文件预览
func PreviewFile(c *gin.Context) {
	type Request struct {
		Path string `json:"path"`
	}
	var req Request
	if err := c.ShouldBindJSON(&req); err != nil {
	    JsonResponse(c, http.StatusInternalServerError, "参数错误", nil)
		return
	}
	if req.Path == "" {
		JsonResponse(c, http.StatusBadRequest, "路径参数缺失", nil)
		return
	}
	// 验证路径安全性
	fullPath := filepath.Clean(filepath.Join(UploadPath, req.Path))
	if !strings.HasPrefix(fullPath, UploadPath) {
	    JsonResponse(c, http.StatusBadRequest, "非法路径访问", nil)
		return
	}
	// 检查文件是否存在
	info, err := os.Stat(fullPath)
	if err != nil {
		if errors.Is(err, os.ErrNotExist) {
		    JsonResponse(c, http.StatusBadRequest, "文件不存在", nil)
			return
		}
		JsonResponse(c, http.StatusBadRequest, "文件访问失败", nil)
		return
	}
	// 处理目录请求
	if info.IsDir() {
	    JsonResponse(c, http.StatusInternalServerError, "不能预览目录", nil)
		return
	}
	// 根据文件类型处理
    ext := strings.ToLower(filepath.Ext(fullPath))
    mimeType := mime.TypeByExtension(ext)
    fileurl := fmt.Sprintf("%suploads/%s", configs.MyURL, req.Path)
    if AllowedExts[ext] {
        if info.Size() > MaxPreviewSize {
            JsonResponse(c, http.StatusRequestEntityTooLarge, "文件过大无法预览", nil)
            return
        }
        content, err := os.ReadFile(fullPath)
        if err != nil {
            log.Printf("文件读取失败: %v", err)  // 添加日志记录
            JsonResponse(c, http.StatusInternalServerError, "文件读取失败", nil)
            return
        }
        JsonResponse(c, http.StatusOK, "获取成功", gin.H{"type": "text", "content": string(content),})
    }else{
        JsonResponse(c, http.StatusOK, "获取成功", gin.H{"type": mimeType, "url":  fileurl,})
    }
}
	
// RenameRequest 重命名请求参数
type RenameRequest struct {
    OldPath string `json:"oldPath"`     //fullPath: "blog/1740391513427845081.jpg"
    NewName string `json:"newName"`     //name: "aa.jpg"
}

// 重命名文件（统一响应格式）
func RenameFile(c *gin.Context) {
    var req RenameRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        JsonResponse(c, http.StatusBadRequest, "参数错误", nil)
        return
    }
    oldFullPath := filepath.Clean(filepath.Join(UploadPath, req.OldPath))
    if !isValidPath(oldFullPath) {
        JsonResponse(c, http.StatusBadRequest, "非法路径", nil)
        return
    }
    newPath := filepath.Join(filepath.Dir(req.OldPath), req.NewName)
    newFullPath := filepath.Clean(filepath.Join(UploadPath, newPath))
    if !isValidPath(newFullPath) || strings.Contains(req.NewName, "/") {
        JsonResponse(c, http.StatusBadRequest, "非法文件名", nil)
        return
    }
    if err := os.Rename(oldFullPath, newFullPath); err != nil {
        JsonResponse(c, http.StatusInternalServerError, "重命名失败", nil)
        return
    }
    JsonResponse(c, http.StatusOK, "操作成功", gin.H{"newPath": newPath})
}
	
// 复制、移动文件操作请求
type FileOperationRequest struct {
    Source  string `json:"source"`
    Target  string `json:"target"`
    Overwrite bool `json:"overwrite"`
}

func MoveFile(c *gin.Context) {
    handleFileOperation(c, true)
}

func CopyFile(c *gin.Context) {
    handleFileOperation(c, false)
}

func handleFileOperation(c *gin.Context, isMove bool) {
    var req FileOperationRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        JsonResponse(c, http.StatusInternalServerError, "参数错误", nil)
        return
    }
    // 路径安全检查
    srcPath := filepath.Clean(filepath.Join(UploadPath, req.Source))
    dstPath := filepath.Clean(filepath.Join(UploadPath, req.Target))
    if !isValidPath(srcPath) || !isValidPath(dstPath) {
        JsonResponse(c, http.StatusInternalServerError, "非法路径访问", nil)
        return
    }
    // 执行操作
    if isMove {
        if err := os.Rename(srcPath, dstPath); err != nil {
            handleFileError(c, err, "移动")
            return
        }
    } else {
        if err := mancopyFile(srcPath, dstPath, req.Overwrite); err != nil {
            handleFileError(c, err, "复制")
            return
        }
    }
    JsonResponse(c, http.StatusOK, "操作成功", nil)
}

func mancopyFile(src, dst string, overwrite bool) error {
    if !overwrite && fileExists(dst) {
        return errors.New("目标文件已存在")
    }
    srcFile, err := os.Open(src)
    if err != nil {
        return err
    }
    defer srcFile.Close()
    dstFile, err := os.Create(dst)
    if err != nil {
        return err
    }
    defer dstFile.Close()
    _, err = io.Copy(dstFile, srcFile)
    return err
}

// 压缩和解压文件请求
type ZipRequest struct {
    Files   []string `json:"files"`
    ZipPath string   `json:"zipPath"`
    ZipName string   `json:"zipName"`
}

//压缩打包文件
func CreateZip(c *gin.Context) {
    var req ZipRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        JsonResponse(c, http.StatusBadRequest, "参数错误", nil)
        return
    }
    zipDir := filepath.Clean(filepath.Join(UploadPath, req.ZipPath))
    if !isValidPath(zipDir) {
        JsonResponse(c, http.StatusBadRequest, "非法路径", nil)
        return
    }
    if err := os.MkdirAll(zipDir, 0755); err != nil {
        JsonResponse(c, http.StatusInternalServerError, "目录创建失败", nil)
        return
    }
    zipFilePath := filepath.Join(zipDir, req.ZipName)
    zipFile, err := os.Create(zipFilePath)
    if err != nil {
        JsonResponse(c, http.StatusInternalServerError, "压缩文件创建失败", nil)
        return
    }
    defer zipFile.Close()
    zipWriter := zip.NewWriter(zipFile)
    defer zipWriter.Close()
    for _, f := range req.Files {
        filePath := filepath.Clean(filepath.Join(UploadPath, f))
        if !isValidPath(filePath) {
            continue
        }
        // 获取文件信息
        info, err := os.Stat(filePath)
        if err != nil {
            continue // 记录错误或跳过
        }
        if info.IsDir() {
            // 处理目录：递归添加目录下所有文件
            err := filepath.Walk(filePath, func(path string, info os.FileInfo, err error) error {
                if err != nil {
                    return err
                }
                // 计算相对于UploadPath的相对路径
                relPath, err := filepath.Rel(UploadPath, path)
                if err != nil {
                    return err
                }
                zipPath := filepath.ToSlash(relPath) // 确保使用正斜杠
                // 创建文件头
                header, err := zip.FileInfoHeader(info)
                if err != nil {
                    return err
                }
                header.Name = zipPath
                // 目录需要以/结尾
                if info.IsDir() {
                    header.Name += "/"
                    header.Method = zip.Store
                } else {
                    header.Method = zip.Deflate
                }
                writer, err := zipWriter.CreateHeader(header)
                if err != nil {
                    return err
                }
                // 目录无内容，直接返回
                if info.IsDir() {
                    return nil
                }
                // 写入文件内容
                file, err := os.Open(path)
                if err != nil {
                    return err
                }
                defer file.Close()
                _, err = io.Copy(writer, file)
                return err
            })
            if err != nil {
                JsonResponse(c, http.StatusInternalServerError, "压缩目录失败", nil)
                return
            }
        } else {
            // 处理单个文件
            relPath, err := filepath.Rel(UploadPath, filePath)
            if err != nil {
                continue
            }
            zipPath := filepath.ToSlash(relPath)
            if err := addFileToZip(zipWriter, filePath, zipPath); err != nil {
                JsonResponse(c, http.StatusInternalServerError, "压缩文件失败", nil)
                return
            }
        }
    }
    JsonResponse(c, http.StatusOK, "压缩成功", gin.H{"path": req.ZipPath})
}

// 添加单个文件到ZIP
func addFileToZip(zipWriter *zip.Writer, filePath string, zipName string) error {
    file, err := os.Open(filePath)
    if err != nil {
        return err
    }
    defer file.Close()
    info, err := file.Stat()
    if err != nil {
        return err
    }
    header, err := zip.FileInfoHeader(info)
    if err != nil {
        return err
    }
    header.Name = filepath.ToSlash(zipName) // 确保路径使用正斜杠
    header.Method = zip.Deflate
    writer, err := zipWriter.CreateHeader(header)
    if err != nil {
        return err
    }
    _, err = io.Copy(writer, file)
    return err
}

// 解压文件主函数
func Unzip(c *gin.Context) {
    var req ZipRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        JsonResponse(c, http.StatusBadRequest, "参数错误", nil)
        return
    }
    // 生成 ZIP 文件完整路径
    zipDir := filepath.Clean(filepath.Join(UploadPath, req.ZipPath))
    zipFilePath := filepath.Join(zipDir, req.ZipName)
    // 校验路径合法性
    if !isValidPath(zipDir) || filepath.Ext(req.ZipName) != ".zip" {
        JsonResponse(c, http.StatusBadRequest, "非法的ZIP文件路径", nil)
        return
    }
    targetPath := zipDir // 解压到zip所在目录
    // 执行解压
    if err := tounzip(zipFilePath, targetPath); err != nil {
        JsonResponse(c, http.StatusInternalServerError, "解压失败："+err.Error(), nil)
        return
    }
    JsonResponse(c, http.StatusOK, "解压成功", nil)
}

// 核心解压逻辑
func tounzip(src, dest string) error {
    r, err := zip.OpenReader(src)
    if err != nil {
        return err
    }
    defer r.Close()
    for _, f := range r.File {
        fpath := filepath.Join(dest, f.Name)
        // 防止路径穿越
        if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
            return fmt.Errorf("非法文件路径：%s", fpath)
        }
        // 创建目录
        if f.FileInfo().IsDir() {
            if err := os.MkdirAll(fpath, 0755); err != nil {
                return err
            }
            continue
        }
        // 创建父目录
        if err := os.MkdirAll(filepath.Dir(fpath), 0755); err != nil {
            return err
        }
        // 打开目标文件（立即关闭，不使用 defer）
        outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
        if err != nil {
            return err
        }
        // 从 ZIP 中读取文件内容
        rc, err := f.Open()
        if err != nil {
            outFile.Close()
            return err
        }
        // 写入内容并立即关闭句柄
        _, err = io.Copy(outFile, rc)
        outFile.Close()
        rc.Close()
        if err != nil {
            return err
        }
    }
    return nil
}

// 分片上传结构
type ChunkUpload struct {
    UploadID    string  `json:"uploadId"`
    FileName    string  `json:"fileName"`
    TotalSize   int64   `json:"totalSize"`
    TotalChunks int     `json:"totalChunks"`    // 后端计算，非前端传递
    ChunkSize   int     `json:"chunkSize"`      // 后端计算，非前端传递
    SavePath    string  `json:"savePath"`       // 存储路径（绝对路径）
    UserPath    string  `json:"userPath"`       // 用户原始路径（用于校验）
}

var uploads = make(map[string]*ChunkUpload)

//创建上传，如果types为空则限制文件类型 .pdf,.doc,.docx,.rar,.zip,.ppt,.pptx
func StartUpload(c *gin.Context) {
    // 高风险禁止类型
    highRiskTypes := []string{
        ".exe", ".dll", ".bat", ".cmd",     // 可执行文件
        ".js", ".jse", ".vbs", ".wsf",     // 脚本文件 
        ".ps1", ".sh",                     // Shell脚本
        ".iso", ".img",                    // 磁盘映像
        ".msi", ".psd",                    // 安装包/Photoshop
        ".hta", ".scr",                    // 特殊可执行
        ".lnk", ".url",                    // 快捷方式
        ".php", ".py", ".pl",              // 补充脚本类型
        ".jar", ".class",                   // Java可执行
    }
    type StartRequest struct {
        FileName  string `json:"fileName" binding:"required"`
        TotalSize int64  `json:"totalSize" binding:"required"`
        Path      string `json:"path"`
    }
    var req StartRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        JsonResponse(c, http.StatusBadRequest, "参数错误", nil)
        return
    }
    // 文件类型验证（黑名单机制）
    ext := strings.ToLower(filepath.Ext(req.FileName))
    for _, bannedExt := range highRiskTypes {
        if ext == bannedExt {
            JsonResponse(c, http.StatusForbidden, "禁止上传危险文件类型: "+ext, highRiskTypes)
            return
        }
    }
    // 大小限制
    if req.TotalSize > 500*1024*1024 {
        JsonResponse(c, http.StatusRequestEntityTooLarge, "文件大小超过500MB限制", nil)
        return
    }
    // 转换为实际存储路径
    savePath := filepath.Join(UploadPath, req.Path) 
    if err := os.MkdirAll(savePath, 0755); err != nil {
        JsonResponse(c, http.StatusInternalServerError, "目录创建失败", nil)
        return
    }
    // 后端主动计算分片参数（不再依赖前端传递）
    chunkSize := 5 * 1024 * 1024 // 默认分片大小5MB
    totalChunks := int((req.TotalSize + int64(chunkSize) - 1) / int64(chunkSize)) // 计算总分片数
    // 生成唯一上传ID
    uploadID := generateUUID()
    // 存储上传任务信息
    uploads[uploadID] = &ChunkUpload{
        UploadID:    uploadID,
        FileName:    req.FileName,
        TotalSize:   req.TotalSize,
        TotalChunks: totalChunks,
        ChunkSize:   chunkSize,
        SavePath:    savePath,
        UserPath:    req.Path,
    }
    JsonResponse(c, http.StatusOK, "操作成功", gin.H{"uploadId":  uploadID,"chunkSize": chunkSize,})
}

//分片上传
func UploadChunk(c *gin.Context) {
    uploadID := c.PostForm("uploadId")
    chunkIndex := c.PostForm("chunkIndex")
    _, exists := uploads[uploadID]
    if !exists {
        JsonResponse(c, http.StatusBadRequest, "无效的上传ID", nil)
        return
    }
    file, err := c.FormFile("chunk")
    if err != nil {
        JsonResponse(c, http.StatusBadRequest, "分片获取失败", nil)
        return
    }
    // 保存分片到临时存储路径为：tmp/chunks/{uploadID}/{chunkIndex}
    chunkDir := filepath.Join(TempPath, uploadID)
    if err := os.MkdirAll(chunkDir, 0755); err != nil {
        JsonResponse(c, http.StatusInternalServerError, "临时目录创建失败", nil)
        return
    }
    chunkPath := filepath.Join(chunkDir, chunkIndex)
    if err := c.SaveUploadedFile(file, chunkPath); err != nil {
        JsonResponse(c, http.StatusInternalServerError, "分片保存失败", nil)
        return
    }
    JsonResponse(c, http.StatusOK, "分片上传成功", nil)
}

//上传合并
func CompleteUpload(c *gin.Context) {
    uploadID := c.Query("uploadId")
    upload, exists := uploads[uploadID]
    if !exists {
        JsonResponse(c, http.StatusBadRequest, "无效的上传ID", nil)
        return
    }
    // 1. 合并分片
    finalPath := filepath.Join(upload.SavePath, upload.FileName)
    finalFile, err := os.Create(finalPath)
    if err != nil {
        JsonResponse(c, http.StatusInternalServerError, "文件创建失败", nil)
        return
    }
    defer finalFile.Close()
    for i := 0; i < upload.TotalChunks; i++ {
        chunkPath := filepath.Join(TempPath, uploadID, fmt.Sprintf("%d", i))
        chunkFile, err := os.Open(chunkPath)
        if err != nil {
            JsonResponse(c, http.StatusInternalServerError, "分片读取失败:"+err.Error(), nil)
            return
        }
        _, err = io.Copy(finalFile, chunkFile)
        chunkFile.Close()
        if err != nil {
            JsonResponse(c, http.StatusInternalServerError, "分片合并失败:"+err.Error(), nil)
            return
        }
        os.Remove(chunkPath)
    }
    // 2. 合并完成后进行文件校验
    isFake, err := isFakeFileType(finalPath)
    if err != nil {
        // 校验失败时清理文件
        os.Remove(finalPath)
        os.RemoveAll(filepath.Join(TempPath, uploadID))
        JsonResponse(c, http.StatusInternalServerError, "文件校验失败", nil)
        return
    }
    if isFake {
        // 检测到伪造类型时清理文件
        os.Remove(finalPath)
        os.RemoveAll(filepath.Join(TempPath, uploadID))
        JsonResponse(c, http.StatusBadRequest, "文件类型与扩展名不符，禁止上传", gin.H{"filename": upload.FileName})
        return
    }
    // 3. 校验通过后完成上传
    delete(uploads, uploadID)
    os.RemoveAll(filepath.Join(TempPath, uploadID)) // 清理临时目录
    JsonResponse(c, http.StatusOK, "上传成功", gin.H{"path": filepath.Join(upload.UserPath, upload.FileName)})
}

// 检查文件实际类型与扩展名是否匹配
func isFakeFileType(filePath string) (bool, error) {
    // 需要严格校验的扩展名与MIME类型映射（根据业务需求补充）
    var mimeWhitelist = map[string][]string{
        ".jpg":  {"image/jpeg"},
        ".jpeg": {"image/jpeg"},
        ".png":  {"image/png"},
        ".gif":  {"image/gif"},
        ".webp": {"image/webp"},
        ".bmp":  {"image/bmp"},
        ".tiff": {"image/tiff"},
    }
    // 打开文件
    file, err := os.Open(filePath)
    if err != nil {
        return false, err
    }
    defer file.Close()
    // 读取前512字节用于类型检测
    buf := make([]byte, 512)
    n, err := file.Read(buf)
    if err != nil && err != io.EOF {
        return false, err
    }
    buf = buf[:n]
    // 检测文件类型
    kind, err := filetype.Match(buf)
    if err != nil {
        return false, err
    }
    // 获取文件扩展名
    ext := strings.ToLower(filepath.Ext(filePath))
    // 如果扩展名在需要校验的列表中
    if expectedMimes, ok := mimeWhitelist[ext]; ok {
        fmt.Printf("检测到文件扩展名: %s, 实际MIME类型: %s\n", ext, kind.MIME.Value)
        for _, mime := range expectedMimes {
            if kind.MIME.Value == mime {
                return false, nil // 类型匹配
            }
        }
        return true, nil // 检测到类型不匹配
    }
    // 不需要校验的类型直接通过
    return false, nil
}

// 全局定义添加互斥锁
var mutex = &sync.Mutex{}
// 上传失败清理分片
func UploadAbort(c *gin.Context) {
    // 获取上传ID
    var req struct {
        UploadID string `json:"uploadId" binding:"required"`
    }
    if err := c.ShouldBindJSON(&req); err != nil {
        JsonResponse(c, http.StatusBadRequest, "参数错误", nil)
        return
    }
    // 加锁保证并发安全
    mutex.Lock()
    defer mutex.Unlock()
    // 检查上传任务是否存在
    upload, exists := uploads[req.UploadID]
    if !exists {
        JsonResponse(c, http.StatusBadRequest, "无效的上传ID", nil)
        return
    }
    // 清理分片文件
    errorCount := 0
    for i := 0; i < upload.TotalChunks; i++ {
        chunkPath := filepath.Join(TempPath, fmt.Sprintf("%s_%d", req.UploadID, i))
        if _, err := os.Stat(chunkPath); err == nil {
            if err := os.Remove(chunkPath); err != nil {
                log.Printf("删除分片失败: %s, 错误: %v", chunkPath, err)
                errorCount++
            }
        }
    }
    // 清理上传记录
    delete(uploads, req.UploadID)
    // 处理清理结果
    if errorCount > 0 {
        log.Printf("分片清理不完全，失败数量: %d", errorCount)
        JsonResponse(c, http.StatusInternalServerError, fmt.Sprintf("部分分片清理失败（%d个）", errorCount), nil)
        return
    }
    JsonResponse(c, http.StatusOK, "分片清理完成", nil)
}

// 文件分享
type FileShare struct {
	Token     string    `json:"token"`
	FilePath  string    `json:"filePath"`
	ExpiresAt time.Time `json:"expiresAt"`
	Password  string    `json:"password,omitempty"` // 添加密码字段，omitempty表示可选
}

var shares = make(map[string]*FileShare)
// 创建分享
func GenerateShareLink(c *gin.Context) {
	type Request struct {
		Path        string `json:"path"`
		ExpireHours int    `json:"expirehours"`
		Password    string `json:"password,omitempty"` // 添加密码字段
	}
	var req Request
	if err := c.ShouldBindJSON(&req); err != nil {
		JsonResponse(c, http.StatusBadRequest, "参数错误", nil)
		return
	}
	// 检查文件路径
	fullPath := filepath.Join(UploadPath, req.Path)
	fileInfo, err := os.Stat(fullPath)
	if err != nil {
		if os.IsNotExist(err) {
			JsonResponse(c, http.StatusNotFound, "文件不存在", nil)
		} else {
			JsonResponse(c, http.StatusInternalServerError, "文件访问失败", nil)
		}
		return
	}
	// 禁止分享目录
	if fileInfo.IsDir() {
		JsonResponse(c, http.StatusBadRequest, "不能分享目录", nil)
		return
	}
	// 设置默认有效期
	expireHours := 24
	if req.ExpireHours > 0 {
		expireHours = req.ExpireHours
	}
	// 生成令牌
	token := generateSecureToken()
	shares[token] = &FileShare{
		Token:     token,
		FilePath:  req.Path,
		ExpiresAt: time.Now().Add(time.Duration(expireHours) * time.Hour),
		Password:  req.Password,
	}
	// 返回结果（不包含密码）
	JsonResponse(c, http.StatusOK, "分享成功", gin.H{
		"url":     fmt.Sprintf("%sv1/getshare/%s", configs.MyURL, token),
		"expires": shares[token].ExpiresAt.Format(time.RFC3339),
		"secured": req.Password != "", // 提示是否需要密码
	})
}

// 打开分享的文件
func HandleShare(c *gin.Context) {
    token := c.Param("token")
    share, exists := shares[token]
    // 链接有效性检查（保持不变）
    if !exists || time.Now().After(share.ExpiresAt) {
        c.HTML(http.StatusNotFound, "share_error.html", gin.H{
            "Title":   "链接已失效",
            "Message": "分享链接不存在或已过期",
        })
        return
    }
    // 增强的下载请求处理
    if c.Query("download") == "true" {
        // 检查密码验证状态
        authCookie, _ := c.Cookie("file_auth_" + token)
        if authCookie != share.Password { // 简单验证逻辑
            c.AbortWithStatus(http.StatusForbidden)
            return
        }
        // 文件检查逻辑（保持不变）
        filePath := filepath.Join(UploadPath, share.FilePath)
        if _, err := os.Stat(filePath); os.IsNotExist(err) {
            c.HTML(http.StatusNotFound, "share_error.html", gin.H{
                "Title":   "文件不存在",
                "Message": "请求的文件已被删除或移动",
            })
            return
        }
        // 下载完成后清除cookie
        c.SetCookie("file_auth_" + token, "", -1, "", "", false, true)
        c.FileAttachment(filePath, filepath.Base(share.FilePath))
        return
    }
    // 密码验证逻辑增强
    if share.Password != "" {
        providedPassword := c.Query("password")
        if providedPassword != share.Password {
            c.HTML(http.StatusUnauthorized, "share_error.html", gin.H{
                "Title":    "需要密码验证",
                "Message":  "请输入密码，如果正确会自动下载文件",
                "ShowForm": true,
            })
            return
        }
        // 设置验证cookie（有效期5分钟）
        c.SetCookie("file_auth_" + token, share.Password, 300, "", "", false, true)
        c.HTML(http.StatusOK, "share_error.html", gin.H{
            "Title":   "验证成功",
            "Message": "✓ 密码验证通过",
            "Success": true,
        })
        return
    }
    // 无密码直接下载
    filePath := filepath.Join(UploadPath, share.FilePath)
    c.FileAttachment(filePath, filepath.Base(share.FilePath))
}

// 生成安全令牌（保持不变）
func generateSecureToken() string {
	b := make([]byte, 16)
	if _, err := rand.Read(b); err != nil {
		return fmt.Sprintf("%d", time.Now().UnixNano())
	}
	return fmt.Sprintf("%x", b)
}